package mars;

import java.awt.image.WritableRaster;
import java.io.File;
import javax.imageio.ImageIO;

public class ImageFilter {

    public static final int X = 0;
    public static final int Y = 1;
    private WritableRaster raster;
    private float[][][] currentETF;
    private float[][][] newETF;
    private float[][] magnitude;
    private int[][] pixelsRuido;
    private int width;
    private int height;

    public void setRaster(WritableRaster raster) {
        this.raster = raster;
        this.width = raster.getWidth();
        this.height = raster.getHeight();
        this.currentETF = new float[width][height][2];
        this.newETF = new float[width][height][2];
        this.magnitude = new float[width][height];
        this.pixelsRuido = new int[width][height];
    }

    public void FDoG(int n, int r, boolean useMIPS) throws Exception {
        if (raster == null) {
            return;
        }

        sobel();

        n = 3;
        r = 7;

        for (int i = 0; i < n; i++) {
            getVectorField(r);
            System.out.println("ETF " + (i + 1));
        }

        WritableRaster rasterIn = ImageIO.read(new File("./GiantNoise.jpg")).getRaster();
        int rgb[] = new int[4];

        for (int i = 0; i < width; i++) {
            for (int j = 0; j < height; j++) {
                rasterIn.getPixel(i, j, rgb);
                pixelsRuido[i][j] = rgb[0];
            }
        }

        Float[][] pixels = null;
        Float min = new Float(0f);
        Float max = new Float(0f);

        if (useMIPS) {
            LineIntegralConvolutionMIPS licMIPS = new LineIntegralConvolutionMIPS(pixelsRuido, currentETF.length, currentETF[0].length, currentETF);
            pixels = Mars.execute_MIPS(licMIPS.toString(), width, height);

            min = Mars.min;
            max = Mars.max;
            
            System.out.println("Min " + min);
            System.out.println("MAx " + max);

            for(int i = 0; i <width; i++){
                for(int j = 0; j < height; j++){
                    System.out.printf("%4.4f ", pixels[i][j]);
                }
                System.out.println("");
            }

        } else {
            LineIntegralConvolution lic = new LineIntegralConvolution(pixelsRuido, currentETF.length, currentETF[0].length, currentETF);
            pixels = lic.getFlow();
            min = lic.getMin();
            max = lic.getMax();
        }

        //Normalização da imagem
        for (int i = 0; i < width; i++) {
            for (int j = 0; j < height; j++) {
                if (pixels[i][j] == 0) {
                    System.out.println("MOPA");
                }
                rgb[0] = (int) Math.round(((float) (pixels[i][j] - min) / (float) (max - min)) * 255);
                rgb[1] = rgb[0];
                rgb[2] = rgb[0];
                raster.setPixel(i, j, rgb);
            }
        }
    }

    private int[][] getPixels() {
        int[][] I = new int[width + 2][height + 2];
        int[] rgb = new int[4];
        int c1, c2;

        for (int i = 0; i < width + 2; i++) {
            for (int j = 0; j < height + 2; j++) {
                c1 = (i == 0) ? 1
                        : (i == width + 1) ? -1 : 0;
                c2 = (j == 0) ? 1
                        : (j == height + 1) ? -1 : 0;

                raster.getPixel(i - 1 + c1, j - 1 + c2, rgb);
                I[i][j] = (int) Math.round((rgb[0] + rgb[1] + rgb[2]) / 3.0);
            }
        }

        return I;
    }

    private void sobel() {
        int[][] I = getPixels();
        int convX = 0, convY = 0;
        float min = Float.MAX_VALUE, max = -Float.MAX_VALUE, g = 0;

        //Varredura da imagem para obtenção dos valores máximos e mínimos
        for (int i = 1; i < width + 1; i++) {
            for (int j = 1; j < height + 1; j++) {
                //Componente X do vetor tangente
                convX = +I[i - 1][j - 1] - I[i - 1][j + 1]
                        + 2 * I[i][j - 1] - 2 * I[i][j + 1]
                        + I[i + 1][j - 1] - I[i + 1][j + 1];

                //Componente Y do vetor tangente
                convY = -I[i - 1][j - 1] - 2 * I[i - 1][j] - I[i - 1][j + 1]
                        + I[i + 1][j - 1] + 2 * I[i + 1][j] + I[i + 1][j + 1];

                //Magnitude do vetor tangente
                g = (float) Math.sqrt(convX * convX + convY * convY);

                magnitude[i - 1][j - 1] = g;
                if (g != 0) {
                    currentETF[i - 1][j - 1][X] = convX / g;
                    currentETF[i - 1][j - 1][Y] = convY / g;
                } else {
                    currentETF[i - 1][j - 1][X] = 1;
                    currentETF[i - 1][j - 1][Y] = 0;
                }

                //Acha os valores mínimo e máximo
                if (g < min) {
                    min = g;
                } else if (g > max) {
                    max = g;
                }
            }
        }

        //Normalização
        for (int i = 0; i < width; i++) {
            for (int j = 0; j < height; j++) {
                magnitude[i][j] = (magnitude[i][j] - min) / (max - min);
            }
        }
    }

    private void getVectorField(int r) {
        float[] sum = new float[2];
        int x1, x2, y1, y2, di, dj;
        float[] v1, v2;
        float magnitudeWeight, directionWeight;
        float k;

        for (int i = 0; i < width; i++) {
            for (int j = 0; j < height; j++) {
                x1 = (i - r < 0) ? 0 : i - r;
                x2 = (i + r >= width) ? width - 1 : i + r;
                y1 = (j - r < 0) ? 0 : j - r;
                y2 = (j + r >= height) ? height - 1 : j + r;

                sum[X] = 0;
                sum[Y] = 0;
                v1 = currentETF[i][j];

                for (int x = x1; x < x2; x++) {
                    for (int y = y1; y < y2; y++) {
                        di = x - i;
                        dj = y - j;

                        if (di * di + dj * dj > r * r) {
                            continue;
                        }

                        v2 = currentETF[x][y];
                        magnitudeWeight = (magnitude[x][y] - magnitude[i][j] + 1) / 2.0f;
                        directionWeight = v1[X] * v2[X] + v1[Y] * v2[Y];

                        sum[X] += v2[X] * magnitudeWeight * directionWeight;
                        sum[Y] += v2[Y] * magnitudeWeight * directionWeight;
                    }
                }
                k = (float) Math.sqrt(sum[X] * sum[X] + sum[Y] * sum[Y]);

                if (k != 0) {
                    newETF[i][j][X] = (sum[X] / k);
                    newETF[i][j][Y] = (sum[Y] / k);
                }
            }
        }

        for (int i = 0; i < width; i++) {
            for (int j = 0; j < height; j++) {
                currentETF[i][j][X] = newETF[i][j][X];
                currentETF[i][j][Y] = newETF[i][j][Y];
            }
        }
    }
}
